package com.pet.system.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.pet.common.model.PageModel;
import com.pet.system.constant.OperationType;
import com.pet.system.constant.UserStatus;
import com.pet.system.domain.entity.SysRoleUser;
import com.pet.system.domain.entity.SysUser;
import com.pet.system.domain.entity.SysUserResource;
import com.pet.system.domain.vo.SysUserVO;
import com.pet.system.mapper.SysRoleUserMapper;
import com.pet.system.mapper.SysUserMapper;
import com.pet.system.mapper.SysUserResourceMapper;
import com.pet.system.security.jwt.SafeConfig;
import com.pet.system.service.ISysUserService;
import lombok.AllArgsConstructor;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.Serializable;
import java.util.*;
import java.util.stream.Collectors;

/** @author Centaurea */
@Service
@AllArgsConstructor
public class SysUserServiceImpl implements ISysUserService {
  private final SafeConfig safeConfig;
  private final SysUserMapper userMapper;
  private final SysRoleUserMapper roleUserMapper;
  private final SysUserResourceMapper userResourceMapper;

  @Override
  public PageModel<SysUser> query(PageModel<SysUser> page, Map<String, Object> params) {
    QueryWrapper<SysUser> queryWrapper = new QueryWrapper<>();
    if (params.get("status") != null) {
      queryWrapper.like(SysUser.COL_STATUS, params.get("status"));
    }
    if (params.get("searchWord") != null) {
      queryWrapper
          .like(SysUser.COL_NICKNAME, params.get("searchWord"))
          .or()
          .like(SysUser.COL_STATUS, params.get("searchWord"));
    }
    Page<SysUser> sysUserPage =
        userMapper.selectPage(new Page<>(page.getPageNo(), page.getLimit()), queryWrapper);
    page.setTotal(sysUserPage.getTotal());
    page.setRecords(sysUserPage.getRecords());
    return page;
  }

  @Override
  @Transactional(rollbackFor = Exception.class)
  public SysUser save(SysUserVO userDto) {
    String password = userDto.getPassword();
    if (StringUtils.isEmpty(password)) {
      password = safeConfig.getUserPassword();
    }
    userDto.setId(null);
    userDto.setSalt(
        DigestUtils.md5Hex(
            UUID.randomUUID().toString() + System.nanoTime() + UUID.randomUUID().toString()));
    userDto.setStatus(
        userDto.getStatus() == null ? UserStatus.VALID.getStatus() : userDto.getStatus());
    userDto.setPassword(passwordEncoder(password, userDto.getSalt()));
    userMapper.insert(userDto);
    saveUserRoles(userDto.getId(), userDto.getRoleIds(), OperationType.ADD);
    saveUserResource(userDto.getId(), userDto.getResourceIds(), OperationType.ADD);
    return userDto;
  }

  @Override
  public SysUser update(SysUserVO userDto) {
    SysUser user = userMapper.selectById(userDto.getId());
    if (user != null) {
      if (userDto.getStatus() != null
          && Arrays.asList(
                  new Integer[] {
                    UserStatus.DISABLED.getStatus(),
                    UserStatus.VALID.getStatus(),
                    UserStatus.LOCKED.getStatus()
                  })
              .contains(userDto.getStatus())) {
        user.setStatus(userDto.getStatus());
      }
      user.setEmail(userDto.getEmail());
      user.setMobile(userDto.getMobile());
      user.setNickname(userDto.getNickname());
      userMapper.updateById(user);
      // 判断用户角色、资源是否变更，若变更再执行以下逻辑
      List<Integer> roleIds =
          userDto.getRoleIds() == null ? new ArrayList<>() : userDto.getRoleIds();
      List<Integer> resourceIds =
          userDto.getResourceIds() == null ? new ArrayList<>() : userDto.getResourceIds();
      QueryWrapper<SysUserResource> queryWrapper1 = new QueryWrapper<>();
      queryWrapper1.eq(SysUserResource.COL_USER_ID, userDto.getId());
      List<SysUserResource> sysUserResources = userResourceMapper.selectList(queryWrapper1);
      List<Integer> curResourceIds =
          sysUserResources.stream()
              .map(SysUserResource::getResourceId)
              .collect(Collectors.toList());
      QueryWrapper<SysRoleUser> queryWrapper2 = new QueryWrapper<>();
      queryWrapper2.eq(SysRoleUser.COL_USER_ID, userDto.getId());
      List<SysRoleUser> sysRoleUsers = roleUserMapper.selectList(queryWrapper2);
      List<Integer> curRoleIds =
          sysRoleUsers.stream().map(SysRoleUser::getRoleId).collect(Collectors.toList());
      if (resourceIds != null) {
        curResourceIds.sort(sort);
        resourceIds.sort(sort);
        if (!curResourceIds.equals(resourceIds)) {
          saveUserResource(user.getId(), resourceIds, OperationType.UPDATE);
        }
      }
      if (roleIds != null) {
        curRoleIds.sort(sort);
        roleIds.sort(sort);
        if (!curRoleIds.equals(roleIds)) {
          saveUserRoles(user.getId(), roleIds, OperationType.UPDATE);
        }
      }
    }
    return user;
  }

  @Override
  public boolean updateStatus(Integer id, int status) {
    if (id == null
        || !Arrays.asList(
                new Integer[] {
                  UserStatus.DISABLED.getStatus(),
                  UserStatus.VALID.getStatus(),
                  UserStatus.LOCKED.getStatus()
                })
            .contains(status)) {
      throw new IllegalArgumentException("参数不合法");
    }
    SysUser user = userMapper.selectById(id);
    user.setStatus(status);
    int i = userMapper.updateById(user);
    return i == 1;
  }

  @Override
  @Transactional(rollbackFor = Exception.class)
  public boolean delete(String username) {
    SysUser user = this.getByUserName(username);
    if (user != null) {
      userMapper.deleteById(user);
      saveUserRoles(user.getId(), new ArrayList<>(), OperationType.DELETE);
      saveUserResource(user.getId(), new ArrayList<>(), OperationType.DELETE);
      return true;
    }
    return false;
  }

  @Override
  @Transactional(rollbackFor = Exception.class)
  public boolean batchDelete(List<Integer> ids) {
    if (ids == null || ids.size() == 0) {
      return false;
    }
    List<SysUser> sysUsers = userMapper.selectBatchIds(ids);
    for (SysUser user : sysUsers) {
      userMapper.deleteById(user);
      saveUserRoles(user.getId(), new ArrayList<>(), OperationType.DELETE);
      saveUserResource(user.getId(), new ArrayList<>(), OperationType.DELETE);
    }
    return true;
  }

  @Override
  public SysUser getByUserName(String username) {
    QueryWrapper<SysUser> queryWrapper = new QueryWrapper<>();
    queryWrapper.eq(SysUser.COL_USERNAME, username);
    return userMapper.selectOne(queryWrapper);
  }

  @Override
  public SysUser get(Serializable id) {
    return userMapper.selectById(id);
  }

  @Override
  public String passwordEncoder(String credentials, String salt) {
    Object object =
        StringUtils.isEmpty(salt)
            ? new SimpleHash(safeConfig.getHashAlgorithmName(), credentials)
            : new SimpleHash(
                safeConfig.getHashAlgorithmName(),
                credentials,
                salt,
                safeConfig.getHashIterations());
    return object.toString();
  }

  @Override
  public void changePassword(String username, String oldPassword, String newPassword) {
    SysUser user = this.getByUserName(username);
    if (user != null && user.getPassword().equals(oldPassword)) {
      user.setPassword(passwordEncoder(newPassword, user.getSalt()));
      userMapper.updateById(user);
    }
  }

  @Override
  public void resetPassword(String username, String newPassword) {
    SysUser user = this.getByUserName(username);
    if (user != null) {
      user.setPassword(passwordEncoder(newPassword, user.getSalt()));
      userMapper.updateById(user);
    }
  }

  private void saveUserRoles(Integer userId, List<Integer> roleIds, OperationType type) {
    // 获取当前用户的角色资源信息，若一致，则不再后续执行，否则先删除再新增, 这个逻辑处理交给上一层 修改 时
    if (OperationType.DELETE.equals(type) || OperationType.UPDATE.equals(type)) {
      roleUserMapper.deleteById(userId);
    }
    if (roleIds != null && !OperationType.DELETE.equals(type)) {
      List<SysRoleUser> list = new ArrayList<>();
      for (Integer roleId : roleIds) {
        SysRoleUser sysRoleUser = new SysRoleUser();
        sysRoleUser.setUserId(Math.toIntExact(userId));
        sysRoleUser.setRoleId(Math.toIntExact(roleId));
        list.add(sysRoleUser);
      }
      roleUserMapper.batchInsert(list);
    }
  }

  private void saveUserResource(Integer userId, List<Integer> resourceIds, OperationType type) {
    // 获取当前用户的独立资源信息，若一致，则不再后续执行，否则先删除再新增
    if (OperationType.DELETE.equals(type) || OperationType.UPDATE.equals(type)) {
      userResourceMapper.deleteById(userId);
    }
    if (resourceIds != null && !OperationType.DELETE.equals(type)) {
      List<SysUserResource> list = new ArrayList<>();
      for (Integer resourceId : resourceIds) {
        SysUserResource userResource = new SysUserResource();
        userResource.setUserId(Math.toIntExact(userId));
        userResource.setResourceId(Math.toIntExact(resourceId));
        list.add(userResource);
      }
      userResourceMapper.batchInsert(list);
    }
  }

  private final Comparator<Integer> sort = Integer::compareTo;
}
